﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using agsXMPP;
using System.Threading;
using agsXMPP.protocol.client;
using agsXMPP.Xml.Dom;
using agsXMPP.Collections;
using agsXMPP.protocol.iq.roster;

namespace ISIM
{
    public class XmppClass
    {
        public string protocolName;
        private Jid userJid;
        private string userPassword;
        private string serverDomain;
        public XmppClientConnection xmppClient = new XmppClientConnection();

        public XmppClass(string protocolName, string userJid, string userPassword, string serverDomain = null)
        {
            this.protocolName = protocolName;
            this.userJid = new Jid(userJid);
            this.userPassword = userPassword;
            this.serverDomain = serverDomain;

            xmppClient.Server = this.userJid.Server;
            // tohle se u normálních xmpp (jabber, g+ atp) objevuje nějak takto -> ::ISIM::ads48e8df (ňáký uniqid) u fb se nezobrazí nic
            xmppClient.Resource = "::ISIM::";

            // tohle vůbec, ale vůbec nefunguje
            xmppClient.AutoPresence = true;
            xmppClient.AutoResolveConnectServer = true;
            xmppClient.AutoRoster = true;
            xmppClient.KeepAlive = true;
            xmppClient.KeepAliveInterval = 60;
            xmppClient.SocketConnectionType = agsXMPP.Net.SocketConnectionType.Direct;

            xmppClient.OnXmppConnectionStateChanged += new XmppConnectionStateHandler(xmpp_OnXmppConnectionStateChanged);
            xmppClient.OnPresence += new PresenceHandler(xmpp_OnPresence);
            xmppClient.OnAuthError += new XmppElementHandler(xmpp_OnAuthError);
            xmppClient.OnMessage += new MessageHandler(xmpp_OnMessage);

            xmppClient.OnRosterStart += new ObjectHandler(xmppClient_OnRosterStart);
            xmppClient.OnRosterItem += new XmppClientConnection.RosterHandler(xmpp_OnRosterItem);
            xmppClient.OnRosterEnd += new ObjectHandler(xmppClient_OnRosterEnd);
        }


        /// <summary>
        /// Vytvoří připojení s xmpp serverem a požádá o kontakt list (request roster)
        /// </summary>
        public void connect()
        {
            xmppClient.Open(userJid.User, userPassword);
            xmppClient.OnLogin += xmppClient_OnLogin;
        }

        /// <summary>
        /// Při připojení se oznámí presence
        /// </summary>
        /// <param name="sender"></param>
        void xmppClient_OnLogin(object sender)
        {
            Presence pres = new Presence(ShowType.chat, "Online");
            pres.Type = PresenceType.available;
            xmppClient.Send(pres);

            Console.WriteLine("in xmpp class" + userJid.Server);
        }

        /// <summary>
        /// Zablokuju refreshování contact listu, počkám až se načtou všechny kontakty a on roster end to teprve refreshnu
        /// </summary>
        /// <param name="sender"></param>
        void xmppClient_OnRosterStart(object sender)
        {
            try
            {
                Global.OpenForms.mainForm.contactListView.Invoke(new System.Windows.Forms.MethodInvoker(delegate()
                {
                    Global.OpenForms.mainForm.contactListView.BeginUpdate();
                }));
            }
            catch (InvalidOperationException)
            { }
        }

        /// <summary>
        /// Refresh kontakt listu
        /// </summary>
        /// <param name="sender"></param>
        void xmppClient_OnRosterEnd(object sender)
        {
            try
            {
                Global.OpenForms.mainForm.contactListView.Invoke(new System.Windows.Forms.MethodInvoker(delegate()
                {
                    Global.OpenForms.mainForm.contactListView.EndUpdate();
                }));
            }
            catch (InvalidOperationException)
            { }
        }

        /// <summary>
        /// Vrací kontakty ze server kontakt listu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="item"></param>
        void xmpp_OnRosterItem(object sender, RosterItem item)
        {
            // pokud to nejsu já nebo nemám týpka v kontaktech (buddy)
            if (((protocolName == "gtalk") && (item.Subscription == SubscriptionType.none)) || ((item.Jid.User == userJid.User) && (item.Jid.Server == userJid.Server)))
                return;

            addContact(item);
        }

        /// <summary>
        /// Přidání kontaktu do kontaktlistu
        /// </summary>
        /// <param name="i"></param>
        private void addContact(RosterItem i)
        {
            if (protocolName == "gtalk")
                Console.WriteLine("adding before invoke" + i.Name);
            if ((i.Name == null) || (i.Name.Replace(" ", "").Length == 0))
                i.Name = i.Jid.Bare;

            Global.OpenForms.mainForm.contactListView.Invoke(new System.Windows.Forms.MethodInvoker(delegate()
            {
                if (Global.OpenForms.mainForm.contactListView.Items.ContainsKey(i.Jid.Bare))
                {
                    try
                    {
                        if (Global.OpenForms.mainForm.contactListView.Items[i.Jid.Bare].Text == i.Jid.Bare)
                        {
                            if (!Global.settingsDictionary["contactNames"].ContainsKey(i.Jid.Bare))
                                Global.ISIMFunctions.RenameContact((Global.OpenForms.mainForm.contactListView.Items[i.Jid.Bare] as ContactListViewItem).contact, i.Name);
                            Console.WriteLine("renaming contact");
                        }
                        (Global.OpenForms.mainForm.contactListView.Items[i.Jid.Bare] as ContactListViewItem).contact.name = i.Name;
                    }
                    catch (NullReferenceException)
                    {
                        Console.WriteLine("cannot rename" + i.Name);
                    }
                }
                else
                {
                    if (protocolName == "gtalk")
                        Console.WriteLine("adding" + i.Name);
                    Global.ISIMFunctions.AddContact(new XmppContact(i.Jid.Bare, i.Name, new XmppContact.ContactObject(protocolName), protocolName, ""));
                }
            }));
        }

        /// <summary>
        /// Změna dostupnosti uživatele
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="pres"></param>
        private void xmpp_OnPresence(object sender, Presence pres)
        {
            if ((pres.From == null) || ((pres.From.User == userJid.User) && (pres.From.Server == userJid.Server)))
                return;

            if (pres.Type == PresenceType.subscribe)
            {
                PresenceManager pm = new PresenceManager(xmppClient);
                // abych řekl pravdu, tuhle zprávu sem nikdy neviděl
                if (System.Windows.Forms.MessageBox.Show(Global.LangManager.Translate("authRequest") + ": " + pres.From + "\n" + Global.LangManager.Translate("accept") + "?", Global.LangManager.Translate("authRequest"), System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Exclamation, System.Windows.Forms.MessageBoxDefaultButton.Button1) == System.Windows.Forms.DialogResult.Yes)
                    pm.ApproveSubscriptionRequest(pres.From);
                else
                    pm.RefuseSubscriptionRequest(pres.From);
            }

            Console.WriteLine("presence:" + pres.From);

            Global.OpenForms.mainForm.contactListView.BeginInvoke(new System.Windows.Forms.MethodInvoker(() =>
            {
                try
                {
                    if (!Global.OpenForms.mainForm.contactListView.Items.ContainsKey(pres.From.Bare))
                        Global.ISIMFunctions.AddContact(new XmppContact(pres.From.Bare, pres.From.Bare, new XmppContact.ContactObject(protocolName), protocolName, pres.From.Resource, pres.Type));
                    else
                        (Global.OpenForms.mainForm.contactListView.Items[pres.From.Bare] as ContactListViewItem).contact.client = pres.From.Resource;
                }
                catch (InvalidOperationException)
                { }
            }));

            if (pres.Show.ToString() == "away") pres.Type = PresenceType.probe;
            if (pres.Show.ToString() == "dnd") pres.Type = PresenceType.subscribe;

            if (!Global.Converter.AvatarExists(pres.From.Bare))
                Global.ISIMFunctions.GetXmppContactAvatar(xmppClient, pres.From);

            Global.ISIMFunctions.EditContactAvailability(pres.From.Bare, Global.Converter.GetContactAvailability(pres.Type));
        }

        /// <summary>
        /// Příchozí zpráva
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="msg"></param>
        private void xmpp_OnMessage(object sender, Message msg)
        {
            // got JID -> msg.from
            if ((msg.Body != null) && (msg.Body.Replace(" ", "").Length > 0))
            {
                Global.OpenForms.mainForm.Invoke(new System.Windows.Forms.MethodInvoker(() =>
                {
                    ISIMContact contact;
                    if (Global.OpenForms.mainForm.contactListView.Items.ContainsKey(msg.From.Bare))
                        contact = (Global.OpenForms.mainForm.contactListView.Items[msg.From.Bare] as ContactListViewItem).contact;
                    else
                        contact = new XmppContact(msg.From.Bare, msg.From.Bare, new XmppContact.ContactObject(protocolName), protocolName, msg.From.Resource);

                    Global.ISIMFunctions.AddChatMessage(contact, msg.Body, DateTime.Now, contact.name);
                }));
            }
            else if ((msg.Chatstate == agsXMPP.protocol.extensions.chatstates.Chatstate.composing) || (msg.Chatstate == agsXMPP.protocol.extensions.chatstates.Chatstate.active))
                Global.ISIMFunctions.ContactIsComposing(msg.From.Bare);
        }

        /// <summary>
        /// Změna dostupnosti, vykopli mě hajzli jedni to jim de, nebo sem se přihlásil to jim zas trvá, etc
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="state"></param>
        private void xmpp_OnXmppConnectionStateChanged(object sender, XmppConnectionState state)
        {
            switch (state)
            {
                case XmppConnectionState.Disconnected:
                    Console.WriteLine("xmpp disconnected");

                    if (Global.OpenForms.mainForm.contactListView.InvokeRequired)
                    {
                        try
                        {
                            Global.OpenForms.mainForm.contactListView.Invoke(new System.Windows.Forms.MethodInvoker(delegate()
                            {
                                foreach (ContactListViewItem item in Global.OpenForms.mainForm.contactListView.Items)
                                {
                                    if (item.contact.protocol == protocolName)
                                    {
                                        Global.ISIMFunctions.EditContactAvailability(item.contact.id, Global.Availability.Offline);
                                    }
                                }
                            }));
                        }
                        catch (InvalidOperationException)
                        { }
                    }
                    break;
                case XmppConnectionState.Connected:
                    Console.WriteLine("xmpp connected waiting for auth");
                    break;
                case XmppConnectionState.SessionStarted:
                    Console.WriteLine("xmpp " + protocolName + " sucessfuly connected");
                    Global.ISIMFunctions.CloseMessage(protocolName + "_login_failed");
                    break;
                default:
                    Console.WriteLine("xmpp state:" + state.ToString());
                    break;
            }
        }

        /// <summary>
        /// Většinou špatnej login
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void xmpp_OnAuthError(object sender, Element e)
        {
            Console.WriteLine("xmpp - wrong login");

            Global.ISIMFunctions.ShowMessage(protocolName + "_login_failed", Global.LangManager.Translate("connectionFailed"), protocolName + ": " + Global.LangManager.Translate("connectionFailed") + ". " + Global.LangManager.Translate("checkLoginAndInternetConnection"), System.Windows.Forms.MessageBoxIcon.Error);
        }

        /// <summary>
        /// Odhlášení se od xmpp serveru
        /// </summary>
        public void disconnect()
        {
            Presence p = new Presence(ShowType.chat, "Offline");
            p.Type = PresenceType.unavailable;
            xmppClient.Send(p);

            this.xmppClient.Close();
            Console.WriteLine("disconnected from" + userJid.Server);
        }
    }
}
